#pragma once

#include <iostream>
#include <string>
#include <memory>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>

const std::string pathname = "/tmp";
int proj_id = 0x77;

#define GET_SEM IPC_CREAT // 不给权限
#define BUILD_SEM (IPC_CREAT | IPC_EXCL | 0666)

// 只关注使用和删除
class Semaphore
{
public:
    Semaphore(int semid, int flag) : _semid(semid), _flag(flag)
    {
    }
    void P()
    {
        struct sembuf sb; // 该结构系统提供
        sb.sem_num = 0;
        sb.sem_op = -1;
        sb.sem_flg = SEM_UNDO;
        int n = ::semop(_semid, &sb, 1);
        (void)n;
    }
    void V()
    {
        struct sembuf sb;
        sb.sem_num = 0;
        sb.sem_op = 1;
        sb.sem_flg = SEM_UNDO;
        int n = ::semop(_semid, &sb, 1);
        (void)n;
    }
    ~Semaphore()
    {
        if(_flag == GET_SEM) return;
        // 让信号量自动销毁
        // 如果销毁信号量集合：The argument semnum is ignored
        int n = ::semctl(_semid, 0, IPC_RMID);
        (void)n;
        std::cout << "sem set destroy!" << std::endl;
    }

private:
    int _semid;
    int _flag;
};

using sem_sptr = std::shared_ptr<Semaphore>;



// 使用一下简单的建造者模式,用它来构建单sem
class SemaphoreBuilder
{
public:
    SemaphoreBuilder() : _val(-1)
    {
    }
    SemaphoreBuilder &SetVal(int val)
    {
        _val = val;
        return *this; // 支持连续访问
    }
    sem_sptr Build(int flag)
    {
        // 0. 先做一下简单的合法性判断
        if (_val < 0)
        {
            std::cerr << "you must init first!" << std::endl;
            return nullptr;
        }

        // 1. 申请key值
        key_t k = ::ftok(pathname.c_str(), proj_id);
        if (k < 0)
            exit(1);

        // 2. 根据初始值，创建信号量集合
        int semid = ::semget(k, 1, flag); // 这里让信号量集合中，只创建一个信号量就够用了
        if (semid < 0)
            exit(2);

        if (BUILD_SEM == flag)
        {
            // 3. 初始化信号量
            union semun // 该联合体系统不提供，需要我们自己定义
            {
                int val;               /* Value for SETVAL */
                struct semid_ds *buf;  /* Buffer for IPC_STAT, IPC_SET */
                unsigned short *array; /* Array for GETALL, SETALL */
                struct seminfo *__buf; /* Buffer for IPC_INFO
                                          (Linux-specific) */
            } un;

            un.val = _val; // 设置为初始值

            int n = ::semctl(semid, 0, SETVAL, un);
            if (n < 0)
                exit(3);
        }

        // 4. 创建并返回信号量集合
        return std::make_shared<Semaphore>(semid, flag);
    }
    ~SemaphoreBuilder()
    {
    }

private:
    int _val; // 所有信号量的初始值
};